package cn.com.zero.db.aspect;

import cn.com.zero.db.annotation.DataSource;
import cn.com.zero.db.service.DynamicDataSourceService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;

import java.lang.reflect.Method;


/**
 * @author liuxh
 * @version 1.0.0
 * @Description 切换数据源
 * @createTime 2022/7/15 11:06
 */
@Aspect
@Order(-1) // 保证该AOP在@Transactional之前执行，因为事务执行再后才能生效，否则事务将一直针对默认数据源。
public class DataSourceAOP {

    private static final Logger log = LoggerFactory.getLogger(DataSourceAOP.class);

    // 切点
    @Pointcut("@within(cn.com.zero.db.annotation.DataSource) || @annotation(cn.com.zero.db.annotation.DataSource)")
    public void pointCut() {
    }


    @Before("pointCut()")
    public void changeDataSource(JoinPoint point) throws Throwable {
        DataSource ds = getDataSource(point);
        String dsId = ds.value();
        try {
//            log.debug("切换数据源为：{}......", dsId);
            DynamicDataSourceService.switchDb(dsId);
        } catch (Exception e) {
            log.error("数据源[{}]不存在，使用默认数据源 > {}", ds.value(), point.getSignature());
        }
    }


    @After("pointCut()")
    public void restoreDataSource(JoinPoint point) {
        DataSource ds = getDataSource(point);
//        log.debug("数据库操作使用完毕，重置数据源{}.......", ds.value());
        DynamicDataSourceService.resetDb();
    }


    /**
     * 获取注解的值（先方法，后类）
     *
     * @param point 切点
     * @return 注解
     */
    private DataSource getDataSource(JoinPoint point) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        // 先获取方法的注解值
        Method method = signature.getMethod();
        DataSource annotation = method.getAnnotation(DataSource.class);
        if (annotation == null) {
            // 再获取类上的注解值
            Class declaringType = signature.getDeclaringType();
            annotation = (DataSource) declaringType.getAnnotation(DataSource.class);
        }
        return annotation;
    }

}
